package com.bluexmicro.android.upgradebox.screen.upgrade

import android.Manifest
import android.bluetooth.BluetoothManager
import android.bluetooth.le.ScanResult
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.core.app.ActivityCompat
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bluexmicro.android.coroutineble.*
import com.bluexmicro.android.ota.CoroutineOta
import com.bluexmicro.android.ota.OtaManager
import com.bluexmicro.android.ota.OtaParamHelper
import com.bluexmicro.android.ota.entity.OtaTask
import com.bluexmicro.android.upgradebox.screen.configure.Resource
import com.bluexmicro.android.upgradebox.screen.configure.dataStore
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.io.File
import java.math.BigInteger


class UpgradeViewModel : ViewModel() {

    private val localDataUseCase = LocalDataUseCase()

    ///////////////////////////////////////////////////////////////////////////
    // 第一步：先判断蓝牙是否可用
    ///////////////////////////////////////////////////////////////////////////

    //这里是视觉上决定默认值为true
    val bluetoothEnable = MutableStateFlow(true)
    val scanPermission = MutableStateFlow(true)
    val connectPermission = MutableStateFlow(true)
    val locationEnable = MutableStateFlow(true)
    val locationPermission = MutableStateFlow(true)

    val allReady = combine(
        bluetoothEnable,
        scanPermission,
        connectPermission,
        locationEnable,
        locationPermission
    ) { v1, v2, v3, v4, v5 ->
        v1 && v2 && v3 && v4 && v5
    }

    private fun checkPremises(context: Context): Int {
        val result = context.checkBluetoothState()
        //setDiffValue 减少UI刷新的次数

        bluetoothEnable.setDiffValue((result and FLAG_BLUETOOTH_TOGGLE) > 0)
        scanPermission.setDiffValue((result and FLAG_BLUETOOTH_SCAN) > 0)
        connectPermission.setDiffValue((result and FLAG_BLUETOOTH_CONNECT) > 0)
        locationEnable.setDiffValue((result and FLAG_LOCATION_TOGGLE) > 0)
        locationPermission.setDiffValue((result and FLAG_LOCATION_PERMISSION) > 0)
        return result
    }

    private fun MutableStateFlow<Boolean>.setDiffValue(update: Boolean) {
        val expect = update.not()
        compareAndSet(expect, update)
    }

    fun enableBluetooth(context: Context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || ActivityCompat.checkSelfPermission(
                context,
                Manifest.permission.BLUETOOTH_CONNECT
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
            manager?.adapter?.enable()
        } else {
            Toast.makeText(context, "请先授予「连接蓝牙」的权限", Toast.LENGTH_LONG)
                .show()
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // 获取蓝牙扫描过滤条件
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 启动页面时，加载过滤条件和OTA的文件
     */
    fun loadLocalData(context: Context) = viewModelScope.launch(Dispatchers.IO) {
        localDataUseCase.loadLocalData(context)
    }

    ///////////////////////////////////////////////////////////////////////////
    //
    ///////////////////////////////////////////////////////////////////////////


    private var cycleJob: Job? = null
    fun launchProcess(context: Context, delayTimeMillis: Long = 0L) {
        cycleJob?.takeIf { it.isActive }?.apply { cancel() }
        cycleJob = viewModelScope.launch {
            if (delayTimeMillis > 0) delay(delayTimeMillis)
            var success = true
            execute(context).catch {
                success = false
                Log.e("TAG", "launchProcess:${it.message} ")
            }.collect {

            }
            //todo 在enable location页面时，没有回到OTA页面就开始OTA了
            if (success) delay(1000) else delay(1500)
            Log.e("TAG", "next: $success")
            launchProcess(context)
        }
    }

    fun finishProcess() {
        cycleJob?.takeIf { it.isActive }?.apply { cancel() }
    }

    ///////////////////////////////////////////////////////////////////////////
    //
    ///////////////////////////////////////////////////////////////////////////

    val currentTask = mutableStateOf<UpgradeRecord?>(null)
    val successList = mutableStateListOf<UpgradeRecord>()
    val failedList = mutableStateListOf<UpgradeRecord>()

    private fun execute(context: Context) = flow<Int> {
        // 检查蓝牙权限和功能
        val premiseState = checkPremises(context)
        if (premiseState and STATE_READY_SCAN_CONNECT != STATE_READY_SCAN_CONNECT) {
            throw OtaPremiseException(premiseState)
        }

        // 更新状态
        currentTask.value = null

        // 扫描目标设备
        val target = withTimeout(6000) {
            //tips：timeout设置为6秒的原因是Android有一个防止过度扫描的机制，30s内开始扫描的次数不能超过5次
            val targets = context.scanPeripheral().map {
                it.filter { res ->
//                    var value = true
//                    for (record in successList) {
//                        //过滤掉已经OTA成功的设备
//                        if (record.mac == res.device.address && record.isComplete()) {
//                            value = false
//                            break
//                        }
//                    }
                    localDataUseCase.isTarget(res, context)/* && value*/
                }
            }.filter {
                it.isNotEmpty()
            }.first()
            targets.first()
        }

        // 更新状态
        val infos = localDataUseCase.resources.map { res ->
            //todo 复用失败列表里面的bean
            ResourceInfo(
                res.file.path,
                res.address,
                res.file.length().toInt()
            ).apply {
                rawData = res.rawData
            }
        }
        currentTask.value = UpgradeRecord(mac = target.device.address, resInfoList = infos, 0L)

        //开始OTA
        val coroutineOta = CoroutineOta(context, target.device)
        coroutineOta.startTransaction(true)

        infos.forEach { info ->
            if (info.rawData.isEmpty()) throw OtaException("loadBinData Error")
            val otaTask = OtaTask(info.rawData, info.address)
            val startTimeMillis = System.currentTimeMillis()
            coroutineOta.execute(otaTask, true)
                .catch {
                    info.failureTime.value = info.failureTime.value + 1
                    coroutineOta.endTransaction(true)
                    pushFailureList(currentTask.value!!)
                    throw OtaException(it.message ?: "Unknown Error")
                }
                .collect {
                    info.progress.value = it
                }
            info.costTimeMillis.value = System.currentTimeMillis() - startTimeMillis
        }

        //将缓存区放入结果区
        coroutineOta.endTransaction(true)

        pushSuccessList(currentTask.value!!)
    }

    private fun pushSuccessList(record: UpgradeRecord) {
        val index = successList.indexOfFirst {
            it.mac == record.mac
        }
        if (index in successList.indices) {
            successList[index] = record
        } else {
            successList.add(record)
        }
    }

    private fun pushFailureList(record: UpgradeRecord) {
        val index = failedList.indexOfFirst {
            it.mac == record.mac
        }
        if (index in failedList.indices) {
            failedList[index] = record
        } else {
            failedList.add(record)
        }
    }

}

class OtaPremiseException(private val state: Int) : Exception() {

    override val message: String
        get() = "$state"//todo
}

class OtaException(msg: String) : Exception(msg)

data class UpgradeRecord(
    val mac: String,
    val resInfoList: List<ResourceInfo>,//文件名-文件大小-写入地址-升级成功情况-耗时
    var recordTimeMillis: Long //就是ota开始的时间
) {

    fun isComplete(): Boolean {
        for (resourceInfo in resInfoList) {
            if (resourceInfo.progress.value < 1.0f) {
                return false
            }
        }
        return true
    }
}

data class ResourceInfo(
    val path: String,
    val address: BigInteger?,
    val size: Int
) {
    val progress = mutableStateOf(0f)
    val costTimeMillis = mutableStateOf(0L)
    var rawData = ByteArray(0)//OTA的源数据，
    val failureTime = mutableStateOf(0) //失败的次数
}

